package com.cn.lp;

import com.cn.lp.base.RecordExtStatisticsMode;
import com.cn.lp.domain.DemoDAO;
import com.cn.lp.domain.DemoPO;
import com.cn.lp.dynamic.*;
import com.google.common.collect.Lists;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Box;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.GroupOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

/**
 * 通道解析的地址 ： https://blog.csdn.net/congcong68/article/details/52821159?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param
 * 测试geo相关方法
 * Created by qirong on 2019/4/10.
 */
@RestController
public class TestController {

    @Autowired
    private DemoDAO demoDAO;

    @Autowired
    private MongoDBManager mongoDBManager;

    public static final String FN_INCIDENT_ID = "incidentID";

    private static Logger logger = LoggerFactory.getLogger(TestController.class);

    @ApiOperation(value = "测试保存", notes = "测试保存")
    @GetMapping("testSave")
    @ResponseBody
    public String testSave() {
        DemoPO demoPO = DemoPO.of("test1", "test", new Point(1, 1));
        demoDAO.save(demoPO);
        return "success";
    }

    @ApiOperation(value = "测试动态记录保存", notes = "测试动态记录保存")
    @GetMapping("testDynamicRecord")
    @ResponseBody
    public String testDynamicRecord() {
        List<RecordExt> extList = new ArrayList<>();
        extList.add(RecordExt.of(RecordExtType.TEST_ATR, "test"));
        mongoDBManager.addRecord(System.currentTimeMillis(), "testRecord", extList);
        return "success";
    }

    //嵌套子查询
    @ApiOperation(value = "测试获取动态记录", notes = "测试获取动态记录")
    @PostMapping("testGetDynamicRecord")
    @ResponseBody
    public String testGetDynamicRecord() {
        SearchRequest searchRequest = new SearchRequest();
        List<SearchExtCondition> extConditionList = new ArrayList<>();
        SearchExtCondition extCondition = new SearchExtCondition()
            .setExtType(RecordExtType.TEST_ATR)
            .setConditionType(ConditionType.EQ)
            .setArgList(List.of("test"));
        extConditionList.add(extCondition);
        searchRequest.setPageNum(1)
            .setPageSize(4)
            .setCollectionName("testRecord")
            .setExtConditionList(extConditionList);
        var result = mongoDBManager.searchRecords(searchRequest);
        return result.getTotalElements() + "";
    }

    //嵌套子查询
    @ApiOperation(value = "测试获取", notes = "测试获取")
    @GetMapping("testGet")
    @ResponseBody
    public String testGet() {
        Optional<DemoPO> optional = demoDAO.findById("test1");
        if (optional.isPresent()) {
            return optional.get().getName();
        }
        return "fail";
    }

    @ApiOperation(value = "测试获取范围内数据", notes = "测试获取范围内数据")
    @GetMapping("testWithin")
    @ResponseBody
    public String testWithin() {
        logger.info("test");
        List<DemoPO> dataList = demoDAO.findByLocationWithin(new Circle(new Point(10, 10), 1));
        logger.info("坐标10 范围1之内的个数为 : " + dataList.size());
        dataList = demoDAO.findByLocationWithin(new Circle(new Point(1, 1), 1));
        logger.info("坐标1 范围1之内的个数为 : " + dataList.size());
        return dataList.size() + "";
    }

    @ApiOperation(value = "测试坐标范围内", notes = "测试坐标范围内")
    @GetMapping("testLocationWithin")
    @ResponseBody
    public String testLocationWithin() {
        List<DemoPO> dataList = demoDAO.findByLocationWithin(new Box(new Point(1, 1), new Point(10, 10)));
        logger.info("坐标1,1 到 10, 10 正方形范围之内的个数为 : " + dataList.size());
        return dataList.size() + "";
    }

    public void testCollection() {
        String collectionName = "test";
        // 处理分页
        var pageable = PageRequest.of(0, 5);
        // 处理搜索参数
        Criteria where = new Criteria();
        var match = Aggregation.match(where);
        // 处理统计参数
        var groups = Lists.<GroupOperation>newArrayList();
        var isRoot = true;
        for (var statisticsParamMO : statisticsParamMOs) {
            groups.add(toGroupOperation(statisticsParamMO, false, isRoot));
            isRoot = false;
        }

        // 计算总数聚合参数列表
        var aggregationOperations4Count = Lists.<AggregationOperation>newArrayList();
        aggregationOperations4Count.add(match);
        aggregationOperations4Count.addAll(groups);
        aggregationOperations4Count.add(count().as("count"));
        // 统计总数
        int count = mongoDBManager.aggregateSingleValue(aggregationOperations4Count, collectionName, "count", Integer.class).orElse(0);

        // 计数为0，直接返回
        if (count == 0) {
            return;
//            return new PageImpl<>(List.of(), pageable, count);
        }

        // 处理排序参数
        var sort = sort(toSort(sortParamMO, true));

        // 聚合参数列表
        var aggregationOperations = Lists.<AggregationOperation>newArrayList();
        aggregationOperations.add(match);
        aggregationOperations.add(sort);
        aggregationOperations.addAll(groups);
        aggregationOperations.add(limit(pageable.getPageSize()));
        aggregationOperations.add(skip(pageable.getOffset()));
        // 聚合查询
        var aggResult = mongoDBManager.aggregate(aggregationOperations, collectionName);
    }

    class SortParam {

        List<SortParamItem> itemList = new ArrayList<>();

        public List<SortParamItem> getItemList() {
            return itemList;
        }

        public int getItemCount() {
            return itemList.size();
        }
    }

    class SortParamItem {

        private boolean asc;

        public boolean isAsc() {
            return asc;
        }
    }

    private Sort toSort(SortParam param, boolean isRoot) {
        Sort sort;
        if (param.getItemCount() > 0) {
            List<Sort.Order> orders = param.getItemList().stream()
                .map(item -> {
                    var extType = RecordExtType.get(item.getExtType());
                    var extPath = toFieldPath(extType, isRoot);
                    if (item.isAsc()) {
                        return Sort.Order.asc(extPath);
                    }
                    return Sort.Order.desc(extPath);
                })
                .collect(Collectors.toList());
            sort = Sort.by(orders);
        } else {
            // 默认排序字段
            sort = Sort.by(Sort.Direction.DESC, FN_INCIDENT_ID);
        }
        return sort;
    }

    private String toFieldPath(RecordExtType extType, boolean isRoot) {
        if (isRoot) {
            return extType.getFieldPath(RMRecord.EXT_FIELD_PATH_PREFIX);
        }
        return toAsName(extType);
    }

    /**
     * @param ignoreGroup 是否忽略分组
     */
    private GroupOperation toGroupOperation(StatisticsRecordParamMO statisticsRecordParamMO, boolean ignoreGroup, boolean isRoot) {
        // --分组字段
        var group = ignoreGroup ? group() : group(toGroupFieldPaths(statisticsRecordParamMO, isRoot));
        // --聚合字段
        for (var aggregateRecordExtParamMO : statisticsRecordParamMO.getAggregateExtParamList()) {
            group = appendExtAggregation(group, aggregateRecordExtParamMO, isRoot);
        }
        return group;
    }

    private GroupOperation appendExtAggregation(GroupOperation group, AggregateRecordExtParamMO paramMO, boolean isRoot) {
        var mode = RecordExtStatisticsMode.get(paramMO.getMode());
        var as = toAsName(paramMO);
        if (mode == RecordExtStatisticsMode.COUNT) {
            return group.count().as(as);
        }
        var extType = RecordExtType.get(paramMO.getExtType());
        var fieldPath = toFieldPath(extType, isRoot);
        switch (mode) {
            case SUM:
                return group.sum(fieldPath).as(as);
            case MAX:
                return group.max(fieldPath).as(as);
            case MIN:
                return group.min(fieldPath).as(as);
            case FIRST:
                return group.first(fieldPath).as(as);
            case LAST:
                return group.last(fieldPath).as(as);
        }
        throw new IllegalArgumentException();
    }

}
